﻿package shooter
{
	import exey.tmx.TmxUtils;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Vector3D;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	import net.pixelpracht.tmx.TmxMap;
	import net.pixelpracht.tmx.TmxObject;
	import net.pixelpracht.tmx.TmxObjectGroup;
	import render3d.interfaces.IRender3DAdapter;

	public class Game extends Sprite 
	{
		private const TIME_GAMEPLAY:uint = 30; // seconds 
		
		private var restTime:int;
		private var timer:Timer;
		public var score:int = 0; 
		public var isAlive:Boolean = true;
		
		private var cameraController:CameraController;
		private var heroStartPoint:Vector3D;
		private var monstersController:MonstersController;
		private var bulletsController:BulletsController;
		private var shootTick:int = 0;
		public var gui:GUI;	
		public var level:Level;
		
		public var isMouseDown:Boolean = false;
				
		public var container:DisplayObjectContainer;
		
		public var render:IRender3DAdapter;
		
		public function Game(container:DisplayObjectContainer, render:IRender3DAdapter, tmxUrl:String):void 
		{
			this.container = container;
			container.addChild(this);
			this.render = render;
			loadTmx(tmxUrl);
		}
		
		private function loadTmx(tmxUrl:String):void 
		{
			var loader:URLLoader = new URLLoader(); 
			loader.addEventListener(Event.COMPLETE, onTmxLoaded); 
			loader.load(new URLRequest(tmxUrl)); 		
		}
		
		private function onTmxLoaded(e:Event):void
		{			
			var xml:XML = new XML(e.target.data);
			var tmx:TmxMap = new TmxMap(xml);			
			// Create level
			var tileSize:Number =  tmx.properties.tileSize;
			level = new Level(render, 
							  TmxUtils.CSVtoArray2(tmx.getLayer('ground').toCsv()), 
							  TmxUtils.CSVtoArray2(tmx.getLayer('walls').toCsv()),
							  tileSize,
							  tmx.getTileSet('walls_tileset').firstGID
							  );
			parseTmxObjectGroups(tmx.objectGroups, tileSize/tmx.tileWidth, tileSize/tmx.tileHeight)
		}
		
		private function parseTmxObjectGroups(objectGroupsData:Object, ratioX:Number, ratioY:Number):void 
		{
			var heroData:TmxObject;
			var monsterRespawnsData:Array = []
			for (var name:String in objectGroupsData) 
			{
				var objectGroup:TmxObjectGroup = TmxObjectGroup(objectGroupsData[name])
				var object:TmxObject;
				for (var i:int = 0; i < objectGroup.objects.length; i++) {
					object = TmxObject(objectGroup.objects[i])
					object.x *= ratioX
					object.y *= ratioY				
					if (object.name == "Hero")
						heroData = object
					else if (object.name == "monsters")
						monsterRespawnsData.push(object)				
					trace(object.x, object.y, object.name)
				}
			}
			heroStartPoint = new Vector3D(heroData.x, heroData.y, CameraController.CAMERA_HEIGHT)
			
			// init controllers
			cameraController = new CameraController(render, level);
			render.rotateCamera( CameraController.DEFAULT_ROTATION );
			cameraController.setPosition(heroStartPoint.x, heroStartPoint.y);				
			monstersController = new MonstersController( this, monsterRespawnsData );
			bulletsController = new BulletsController( this );
			
			initUI();
			addListeners();
		}
			
		private function initUI():void 
		{	
			// Init Game UI
			gui = new GUI(this);
			resize(container.stage.stageWidth, container.stage.stageHeight)
		}
		
		private function addListeners():void 
		{
			container.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouse);
			container.stage.addEventListener(MouseEvent.MOUSE_UP, onMouse);
			container.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
			container.stage.addEventListener(KeyboardEvent.KEY_UP, onKey);		
		}
		
		private function removeListeners():void 
		{
			container.stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouse);
			container.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouse);
			container.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
			container.stage.removeEventListener(KeyboardEvent.KEY_UP, onKey);		
		}
		
		//--------------------------------------------------------------------------
		//
		//  GAMEPLAY
		//
		//--------------------------------------------------------------------------
		
		public function restart():void 
		{
			render.rotateCamera( CameraController.DEFAULT_ROTATION )
			cameraController.setPosition(heroStartPoint.x, heroStartPoint.y)

			// nulled game states
			isAlive = true;		
			score = 0;
			gui.scoreTF.text = "0";
			gui.timerTF.text = TIME_GAMEPLAY+":00";
			
			// restart timer
			timer = new Timer(50, TIME_GAMEPLAY * 20);
			timer.addEventListener(TimerEvent.TIMER, onTimer); 
			timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
			restTime = TIME_GAMEPLAY * 100
			timer.start();
			
			// restart monsters
			monstersController.destroyAllMonsters();
			monstersController.respawnMonsters();
		}	
		
		public function addScore():void 
		{
			//trace("addScore")
			score += 1;
			gui.scoreTF.text = "" + score;
			if (!isAlive) 
				gui.finalScoreTF.text = "" + score;
		}
		
		public function gameOver():void 
		{
			timer.stop();
			gui.showGameOverWindow();
		}
		
		public function goToMainMenu():void {
			this.dispatchEvent(new Event("goToMainMenu"));
		}
		
		public function destroy():void {
			removeListeners();
			monstersController.destroyAllMonsters();
			monstersController = null
			cameraController = null
			bulletsController = null
			level = null
			gui = null
			this.parent.removeChild(this);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Listeners 
		//
		//--------------------------------------------------------------------------
		
		public function update():void 
		{
			if (!gui)
				return
			// check shoot tick
			if (isMouseDown) {
				var remainder:int = shootTick % 6 
				if (remainder == 0) {
					bulletsController.shoot();
					gui.resetWeapon()
				}else {
					gui.pullWeapon(remainder);
				}
				shootTick++
			}	
		
			bulletsController.update( monstersController.monsters );
			if (isAlive) {			
				cameraController.update(container.stage.mouseX, container.stage.mouseY);
				monstersController.update();
			}
		}
		
		public function resize(stageWidth:int, stageHeight:int):void 
		{
			// move weapon and final window
			if(gui)
				gui.resize();
		}
		
		private function onMouse(e:MouseEvent):void 
		{
			isMouseDown = e.type == MouseEvent.MOUSE_DOWN;
			cameraController.onMouse( isMouseDown, container.stage.mouseX, container.stage.mouseY);
			if (!isAlive) return;
			shootTick = 1;
			if (isMouseDown)
				gui.pullWeapon();
			else
				gui.resetWeapon();
		}
		
		private function onKey(e:KeyboardEvent):void 
		{
			cameraController.onKey(e);
		}
		
		private function onTimerComplete(e:TimerEvent):void 
		{
			isAlive = false;
			gui.showGameOverWindow();
			timer.removeEventListener(TimerEvent.TIMER, onTimer); 
			timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);			
		}
		
		private function onTimer(e:TimerEvent):void 
		{
			restTime -= 5
			var sec:int = restTime*.01
			var msec:int = restTime-sec*100
			gui.timerTF.text = sec + ":" + msec
		}
	}
}